
//import usart from '../interface/kits/js/usart/@ohos.multimedia.usart';
import {CRC}  from './CRC16.js';


/* 使用：
*totalmsg = concatenate(Uint8Array, totalmsg, submsg) 
*/
function concatenate(resultConstructor, ...arrays) {
    let totalLength = 0;
    for (let arr of arrays) {
        totalLength += arr.length;
    }
    let result = new resultConstructor(totalLength);
    let offset = 0;
    for (let arr of arrays) {
        result.set(arr, offset);
        offset += arr.length;
    }
    return result;
}


//他不处理汉字等字符编码大于255的字符
function stringToArrayBuffer(str) {
	const len=str.length;
	var bytes = new Array(len); 
	for(var i=0;i<len;++i){
		bytes[i]=str.charCodeAt(i);
	}
	return bytes;
}

// 包头1 包头2 包长H 包长L 名称长 名称字符串(含'\0') 参数包数量 参数包1    参数包2    ... 校验位1 校验位2
// 0xFF  0xFF xx    xx    xx    "xxxx\0"          xx        参数包格式 参数包格式      xx     xx


// 参数包格式： 有效数据都采用 小端模式 保存
// 参数包长 类型 有效数据
// xx       yy    ...
// 上表中 xx代表参数包长：长度为有效数据长+1(算上类型yy这个字节)。有效数据长：一个INT为 2个字节长，一个Float为4个字节，字符串长度则为
// 字符的实际长度
// 类型yy 0x01代表 Float32 0x02代表 Int16 0x03代表 String
// 遇到了有整数数组的情况,这时候输入的必须严格是Int16Array
export class ParameterPack{
	//类型的含义
	// FLOAT32=0x01;
	// INT16=0x02;
	// STRING=0x03;
	// INTARRAY=0x04;
	constructor(data){
		console.info("[Wang] ParameterPack::constructor -- data = "+data.toString());
		var typename= typeof(data);
		console.info("[Wang] ParameterPack::constructor -- typename = "+typename.toString());
		switch (typename) {
			case "string":
				console.info("[Wang] ParameterPack::constructor -- do string");
				this.type = 0x03;
				this.data = data;
				break;
			case "number":
				if (data % 1 === 0) {//是整数
					console.info("[Wang] ParameterPack::constructor -- do int16");
					this.type = 0x02;
					this.data = data % (1 << 16);//最多16位
				}
				else {//浮点数
					console.info("[Wang] ParameterPack::constructor -- do float32");
					this.type = 0x01;
					this.data = data;
				}
				break;
			default:{
				console.info("[Wang] ParameterPack::constructor -- type "+typename+"don't match");
				if(data instanceof Int16Array){
					this.type=0x04;
					this.data=data;
				}
				else{
					throw new Error("Invalid Input Type: " + typename + ",which should be string or number");
				}
				
			} 
		}
		console.info("[Wang] ParameterPack::constructor -- type = "
			+this.type.toString()+", data = "+this.data.toString());
	}
	toU8Array(){
		var res;
		console.info("[Wang] ParameterPack::toU8Array -- this.type = "+ this.type.toString());
		switch(this.type){
			case 0x01:{
				res=new Uint8Array(5);
				res[0]=this.type;
				var view = new DataView(res.buffer,1,4);
				view.setFloat32(0, this.data, true);
				
			}break;	
			case 0x02: {
				res = new Uint8Array(3);
				var view = new DataView(res.buffer, 1, 2);
				view.setInt16(0, this.data, true); // 或许默认使用大端法，true设置为小端法
				res[0] = this.type;
			} break;
			case 0x04:{
				const len=this.data.length;
				res=new Uint8Array(2 + 2*len);
				res[0]=this.INT16;
				var view = new DataView(res.buffer,2,2*len);
				for(var i=0;i<len;++i){
					view.setInt16(i,res.data[i],true);
				}
			}break;
			case 0x03:{
				console.info("[Wang] ParameterPack::toU8Array -- tran it to a buffer ");
				const str_array=stringToArrayBuffer(this.data);
				console.info("[Wang] ParameterPack::toU8Array -- tran it to a buffer finish");
				var turn =new Uint8Array(str_array)
				const len =turn.byteLength;
				console.info("[Wang] ParameterPack::toU8Array -- len="+len.toString());
				if(len>255){throw Error("string length "+len.toString()+" too big!");}
				console.info("[Wang] ParameterPack::toU8Array -- begin copying");
				res =new Uint8Array(len +2);

				res[0]=0x03;res[1]=len;
				for(var from_=0;from_<len;++from_){
					const to=from_+2;
					res[to]=turn[from_];
				}

			}break;			
		}
		console.info("[Wang] ParameterPack::toU8Array <<");
		return res;
	}
	decode(U8_raw){
		if(! (this.data instanceof Int16Array)){
			throw new Error("Invalid Input Type: "+typeof(this.data) +",which should be string or number");
		}
		const typecode=U8_raw[1];
		const len=U8_raw[0];
		switch(typecode){
			case 0x01:{
				const v =new DataView(U8_raw.buffer,2,4);
				const float_data=v.getFloat32(0,true);
				return new ParameterPack(float_data);
			}
			case 0x03:{
				const sv=new Uint8Array(U8_raw.buffer,2,len-2);
				const str_data =String.fromCharCode(sv);
				return new ParameterPack(str_data);
			}
			case 0x02:{
				if(len==4){/* 单个字符 */
					const v=new DataView(U8_raw.buffer,2,2);
					const int_data=v.getInt16(0,true);
					return new ParameterPack(int_data);
				}
				else{/* Int16Array 0x04*/
					const v=new DataView(U8_raw.buffer,2,len-2);
					const numlen=(len-2 )/2;
					function* Iterator(){
						var i=0;
						while(i<numlen){
							yield v.getInt16(i*2,true);
						}
						return;
					}
					const arr =new Int16Array(Iterator);
					return new ParameterPack(arr);
				}
			}
		}
	}
};
function array_copy(to_u8array,to_array_init_byte,from_u8array,len){
	var pto=to_array_init_byte;
	for(var j=0;j<len;pto++,j++){
		to_u8array[pto]=from_u8array[j];
	};
	return pto;
}


// 包头1 包头2 包长H 包长L 名称长 名称字符串(含'\0') 参数包数量 参数包1    参数包2    ... 校验位1 校验位2
// 0xFF  0xFF xx    xx    xx    "xxxx\0"          xx        参数包格式 参数包格式      xx     xx
//**包长:**定义为包长L后一个字节开始到通讯包末尾的长度
export class CommunicationPack{
	constructor(name,parameter_pack_array){
		if(name != null && name.length > 0xFF){
			throw Error("Name too long: "+parameter_pack_array.length.toString());
		}
		const para_num = parameter_pack_array.length;
		console.info("[Wang] CommunicationPack::constructor -- paranum = " +para_num.toString());
		if(para_num > 0xFF){throw Error("Too many arameters: "+parameter_pack_array.length.toString());}
		this.name =name;
		this.parameter_packs=parameter_pack_array;
		console.info("[Wang] CommunicationPack::constructor -- name = " +this.name+" para = "+this.parameter_packs);
	}
	toU8Array(){
		console.info("[Wang] CommunicationPack::toU8Array");
		// var U8Arrays=this.parameter_packs.map(
		// 	function(currentValue){return currentValue.toU8Array();}
		// );
		const u8len=this.parameter_packs.length;
		console.info("[Wang] CommunicationPack::toU8Array -- u8len ="+u8len.toString());
		var U8Arrays = new Array(u8len);
		for(var i=0;i<u8len;++i){
			console.info("[Wang] CommunicationPack::toU8Array -- trans para["+i.toString()+"] to U8arr");
			U8Arrays[i]=this.parameter_packs[i].toU8Array();
			console.info("[Wang] CommunicationPack::toU8Array -- trans para["+i.toString()+"] to U8arr complete");
		}
		console.info("[Wang] CommunicationPack::toU8Array -- mapped every small packs to U8Array");
		var paralen=0;
		for(var i=0;i<u8len;++i){
			paralen+= U8Arrays[i].length
		}
		// const paralen=U8Arrays.reduce(
		// 	function(previousValue,currentValue){return previousValue+currentValue.length;}
		// 	,0
		// );
		console.info("[Wang] CommunicationPack::toU8Array -- getparalen");
		var name_and_param_lem;
		const name_is_not_null= this.name != null;
		var name_len;var name_arr;
		if(name_is_not_null){
			const name_buffer=stringToArrayBuffer(this.name);
			name_arr=new Uint8Array(name_buffer);
			name_len=name_arr.length;
			//                名称长   名称   参数数 参数包... 
			name_and_param_lem =1 + name_len + 1 + paralen;
		}
		else{
			//                参数数 参数包... 
			name_and_param_lem =1 + paralen;
		}
		const all_len=name_and_param_lem+6;//两个ff,ff,两个包长,两个校验位
		console.info("[Wang] CommunicationPack::toU8Array -- all_len = "+all_len.toString());
		var res=new Uint8Array(all_len);
		res[0]=res[1]=0xff;//校验位
		res[2]=(name_and_param_lem>>8)& 0xff;	//包长高位
		res[3]=name_and_param_lem& 0xff;//包长低位
		var i=0;
		if(name_is_not_null){
			res[4]=name_len;//名称长
			console.info("[Wang] CommunicationPack::toU8Array -- copying name");
			i=array_copy(res,5,name_arr,name_len);
		}
		else{
			i=4;//不写名称
		}
		res[i]=paralen;/*参数包数量*/ ++i;

		for(var j=0;j<u8len;++j){
			console.info("[Wang] CommunicationPack::toU8Array -- copying element ["+j.toString()+"]");
			i=array_copy(res,i,U8Arrays[j],U8Arrays[j].length)
			console.info("[Wang] CommunicationPack::toU8Array -- copying element ["+j.toString()+"] --ok");
		}

		const para_fin =i;
		//校验和的计算是从包头1(0xFF)到最后一个参数包的最后一个字节
		console.info("[Wang] CommunicationPack::toU8Array -- calculating CRC value");
		const crc_res=CRC.value(res.subarray(0,para_fin));
		console.info("[Wang] CommunicationPack::toU8Array -- calculating CRC value ok");
		res[all_len-2]=crc_res & 0xff;res[all_len-1] = (crc_res>>8)& 0xff;//写入校验位
		console.info("[Wang] CommunicationPack::toU8Array << ");
		return res;
	}
	
	static fromRawData(name,...data){
		var parameter_pack_array=new Array(data.length);
		for(var i=0;i<data.length;++i){
			parameter_pack_array[i]=new ParameterPack(data[i]);
		}
		//var parameter_packs=data.map(function(data){return new ParameterPack(data);})
		return new CommunicationPack(name,parameter_pack_array);
	}
};

//检查类型是调用者的职责
export const Protocol={
	b:CommunicationPack.fromRawData,
	WifiConnect(str_name,str_password){	return this.b("wificonnect",str_name,str_password);},
	IOout(int_portname,int_IO_name){return this.b("IOout",int_portname,int_IO_name);},
	SensorInput(int_portname){return this.b("SensorInput",int_portname);},
	CameraSwitch(int_option){return this.b("CameraSwitch",int_option);},
	openCamera(){return this.b("CameraSwitch",1);},
	closeCamera(){return this.b("CameraSwitch",0);},
	ImageIdent(Int16Array_RGBs){return this.b("ImageIdent",Int16Array_RGBs)},
	FaceReg(int_timeout){return this.b("FaceReg",int_timeout);},
	ColorRange(Int16Array_RGBs,int_HSV_range){
		if(!(Int16Array_RGBs instanceof Int16Array)){throw Error("Invalid type,input is not an Int16Array!");}
		return this.b("ColorRange",Int16Array_RGBs,int_HSV_range);
	},
	CustomColor(int_h_min, int_h_max, int_s_min, int_s_max, int_v_min, int_v_max){
		return this.b("CustomColor",new Int16Array(int_h_min, int_h_max, int_s_min, int_s_max, int_v_min, int_v_max));
	},

};

const UsartTest = {
	parameterPack() {
		var a = new ParameterPack((11<<8) +22 );
		var b = new ParameterPack(456.3);
		var c = new ParameterPack("abc");
		console.log(a.toU8Array(), b.toU8Array(), c.toU8Array());
	},
	communicationPack(){
		var a = new ParameterPack((11<<8) +22 );
		var b = new ParameterPack(456.3);
		var c = new ParameterPack("abc");
		var cpack= new CommunicationPack("test汉字",[a,b,c]);
		var arr=cpack.toU8Array();
		for(var i=0;i<arr.length;++i){

		}
	}

}

UsartTest.communicationPack();



function Uint8ArrayToString(fileData){
    if(!( fileData instanceof Uint8Array)){
        console.error("[Wang] Value is NOT a Uint8Array !!!")
    }
    var dataString = "";
    for (var i = 0; i < fileData.length; i++) {
        dataString += String.fromCharCode(fileData[i]);
    }

    return dataString
}


function stringToUint8Array(str){
    var arr = [];
    for (var i = 0, j = str.length; i < j; ++i) {
        arr.push(str.charCodeAt(i));
    }

    var tmpUint8Array = new Uint8Array(arr);
    return tmpUint8Array
}
